<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第13章 - 国際化とローカライゼーション</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="12-Caching.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="14-Generators.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.13.i18n.and.l10n" id="chapter.13.i18n.and.l10n"></a><h1>第13章 - 国際化とローカライゼーション</h1>

<p>国際的なアプリケーションを開発したことがあるのであれば、テキストの翻訳、ローカルスタンダードとローカライズされた内容のあらゆる面を処理することが悪夢であることはご存じでしょう。幸いにして、symfonyは国際化のすべての面をネイティブに自動化します。</p>

<p>国際化(internationalization)は長い単語なので、開発者はしばしばi18nと表記します(単語の文字数を数えてみれば理由がわかります)。ローカライゼーション(localization - 現地化)はl10nと短縮されます。これらは多言語のWebアプリケーションの2つの異なる面をカバーします。</p>

<p>国際化されたアプリケーションはさまざまな言語もしくはフォーマットで同じ内容のいくつかのバージョンを含みます。たとえば、Webメールのインターフェイスはインターフェイスを変更するだけでいくつもの言語で同じサービスを提供できます; インターフェイスだけ変わります。</p>

<p>ローカライズされたアプリケーションはブラウザーから得られた国の情報にしたがって相異なる情報を含みます。ポータルニュースを考えてみましょう。アメリカからブラウザーで見たとき、アメリカについての最新のヘッドラインを表示します。しかし、フランスからブラウザーで見たとき、ヘッドラインはフランスのニュースに関するものです。ローカライズされたアプリケーションは翻訳内容を提供するだけでなく、1つのローカライズされたバージョンから別のバージョンまで異なった内容を提供します。</p>

<p>全体で、国際化とローカライゼーションを扱うことはアプリケーションがつぎの内容を担当することを意味します:</p>

<ul>
<li>テキストの翻訳(インターフェイス、アセットと内容)</li>
<li>標準とフォーマット(日付、量、数字など)</li>
<li>ローカライズされた内容(国に従う多くのバージョンの任意のオブジェクト)</li>
</ul>

<p>この章ではsymfonyはこれらの要素と国際化とローカライズされたアプリケーションを開発するためにsymfonyを使う方法をカバーします。</p>

<div class="toc">
<dl>
<dt><a href="#user.culture">13.1. ユーザーのculture</a></dt>
<dd><dl>
<dt><a href="#setting.the.default.culture">13.1.1. デフォルトのcultureを設定する</a></dt>
<dt><a href="#changing.the.culture.for.a.user">13.1.2. ユーザーのためのcultureを変更する</a></dt>
<dt><a href="#determining.the.culture.automatically">13.1.3. cultureを自動的に決定する</a></dt>
</dl></dd>
<dt><a href="#standards.and.formats">13.2. 標準規格とフォーマット</a></dt>
<dd><dl>
<dt><a href="#outputting.data.in.the.users.culture">13.2.1. ユーザーのcultureでデータを出力する</a></dt>
<dt><a href="#getting.data.from.a.localized.input">13.2.2. ローカライズされた入力からデータを取得する</a></dt>
</dl></dd>
<dt><a href="#text.information.in.the.database">13.3. データベース内のテキスト情報</a></dt>
<dd><dl>
<dt><a href="#creating.localized.schema">13.3.1. ローカライズされたスキーマを作成する</a></dt>
<dt><a href="#using.the.generated.i18n.objects">13.3.2. 生成された国際化オブジェクトを使う</a></dt>
</dl></dd>
<dt><a href="#interface.translation">13.4. インターフェイスの翻訳</a></dt>
<dd><dl>
<dt><a href="#configuring.translation">13.4.1. 設定の翻訳</a></dt>
<dt><a href="#using.the.translation.helper">13.4.2. 翻訳ヘルパーを使う</a></dt>
<dt><a href="#using.dictionary.files">13.4.3. 辞書ファイルを使う</a></dt>
<dt><a href="#managing.dictionaries">13.4.4. 辞書を管理する</a></dt>
<dt><a href="#handling.other.elements.requiring.translation">13.4.5. 翻訳が必要なそのほかの要素を扱う</a></dt>
<dt><a href="#handling.complex.translation.needs">13.4.6. 複雑な翻訳ニーズを扱う</a></dt>
<dt><a href="#calling.the.translation.helper.outside.a.template">13.4.7. テンプレート外部から翻訳ヘルパーを呼び出す</a></dt>
</dl></dd>
<dt><a href="#summary">13.5. まとめ</a></dt>
</dl>
</div>
<a name="user.culture" id="user.culture"></a><h2>ユーザーのculture</h2>

<p>symfonyにおけるすべての組み込みの国際化機能はcultureと呼ばれるユーザーセッションのパラメーターに基づいています。cultureはユーザーの国と言語の組み合わせで、テキストとcultureに依存する情報を表示する方法を決定します。これはユーザーセッションのなかでシリアライズされるので、cultureはページのあいだで持続します。</p>

<a name="setting.the.default.culture" id="setting.the.default.culture"></a><h3>デフォルトのcultureを設定する</h3>

<p>デフォルトでは、新しいユーザーのcultureは<code>default_culture</code>です。リスト13-1で示されるように、この設定を<code>settings.yml</code>設定ファイルのなかで変更できます。</p>

<p>リスト13-1 - デフォルトのcultureを設定する(<code>frontend/config/settings.yml</code>)</p>

<pre><code>all:
  .settings:
    default_culture: fr_FR
</code></pre>

<blockquote class="note"><p>
  開発期間において、<code>settings.yml</code>ファイルのなかのcultureを変更してもブラウザーの現在のcultureが変更されないことに驚くかもしれません。セッションが前のページからすでにcultureを持っているからです。新しいデフォルトのcultureを持つアプリケーションを見たい場合、ドメインCookieをクリアする、もしくはブラウザーを再起動する必要があります。</p>
</blockquote>

<p>言語と国の両方をcultureに保存しておくことが必要です。なぜなら、フランス、ベルギー、もしくはカナダからの異なったフランス語の翻訳、スペイン、もしくはメキシコからの異なるスペイン語の内容が存在するかもしれないからです。言語はISOの639-1標準規格にしたがって小文字の2文字で表記されます(たとえばenは英語)。ISOの3166-1標準規格にしたがって国は大文字の2文字で表記されます(たとえばGBはイギリス) 。</p>

<a name="changing.the.culture.for.a.user" id="changing.the.culture.for.a.user"></a><h3>ユーザーのためのcultureを変更する</h3>

<p>ユーザーのcultureはブラウジングセッションのあいだに変更できます。たとえば、ユーザーがアプリケーションを英語バージョンからフランス語バージョンに切り替えることを決めたとき、ユーザーがアプリケーションにログインしたとき、ユーザーのオプションに保存されている言語が使われます。<code>sfUser</code>クラスがユーザーのcultureのためにゲッターメソッドとセッターメソッドを提供する理由はそういうわけです。リスト13-2はアクションでこれらのメソッドを使う方法を示しています。</p>

<p>リスト13-2 - アクションのなかでcultureを設定して読みとる</p>

<pre class="php"><span class="co1">// cultureのセッター</span>
<span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setCulture</span><span class="br0">&#40;</span><span class="st_h">'en_US'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// cultureのゲッター</span>
<span class="re0">$culture</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getCulture</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> en_US</pre>

<blockquote class="sidebar"><p class="title">
  <strong>URLのなかのculture</strong></p>
  
  <p>symfonyのローカライゼーションと国際化機能を使うとき、ページは単独のURLに対して異なるバージョンを持つ傾向にあります。これはすべてユーザーセッション次第です。このことによってページが検索エンジンにキャッシュされる、もしくはインデックスに登録されることを防ぎます。</p>
  
  <p>1つの解決方法はcultureがすべてのURLに表示されるようにすることで、翻訳されたページは外部の世界に対して異なるURLとして見なされます。これを実現するには、<code>:sf_culture</code>トークンをアプリケーションの<code>routing.yml</code>のすべてのルールに追加します:</p>

<pre><code>page:
  url: /:sf_culture/:page
  requirements: { sf_culture: (?:fr|en|de) }
  params: ...

article:
  url: /:sf_culture/:year/:month/:day/:slug
  requirements: { sf_culture: (?:fr|en|de) }
  params: ...
</code></pre>
  
  <p>すべての<code>link_to()</code>の中で<code>sf_culture</code>リクエストパラメーターを手動で設定することを避けるために、symfonyはデフォルトのルーティングパラメーターにユーザーのcultureを自動的に追加します。これは内部でも機能します。<code>sf_culture</code>パラメーターがURLのなかで見つかる場合、自動的にユーザーのcultureを変更するからです。</p>
</blockquote>

<a name="determining.the.culture.automatically" id="determining.the.culture.automatically"></a><h3>cultureを自動的に決定する</h3>

<p>多くのアプリケーションにおいて、ユーザーのcultureはブラウザーのオプションに基づいて最初のリクエストで定義されます。ユーザーはブラウザーで受け入れられる言語リストを定義することが可能で、HTTPの<code>Accept-Language</code>ヘッダーにおいて、各リクエストによってこのデータはサーバーに送信されます。<code>sfWebRequest</code>オブジェクトを通してこれをsymfony内部で読みとることができます。たとえば、アクションのなかでユーザーの選択言語のリストを取得するには、つぎのコードを記入します:</p>

<pre class="php"><span class="re0">$languages</span> <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getLanguages</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>HTTPヘッダーは文字列ですが、symfonyは自動的にこれを解析して配列に変換します。前の例ではユーザーの選択言語は<code>$languages[0]</code>でアクセスできます。</p>

<p>サイトのホームページもしくはすべてのページに対するフィルターのなかでこれはユーザーのcultureをユーザーが選択したブラウザーの言語に自動的に設定するさいに便利です。しかしおそらくあなたのWebサイトは限定された言語の一式しかサポートしないので、<code>getPreferredCulture()</code>メソッドを使うほうがベターです。これはユーザーが選択した言語とサポートされる言語を比較することでベストな言語を返します:</p>

<pre class="php"><span class="re0">$language</span> <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getPreferredCulture</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'en'</span><span class="sy0">,</span> <span class="st_h">'fr'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// Webサイトは英語もしくはフランス語で利用できる</span></pre>

<p>マッチする言語が存在する場合、メソッドは最初にサポートされる言語を返します(先の例では<code>en</code>)。</p>

<blockquote class="caution"><p>
  HTTPの<code>Accept-Language</code>ヘッダーはあまり信用できる情報ではありません。ユーザーがそれをブラウザーで修正する方法をほとんど知らないからです。多くの場合、選択されたブラウザー言語はインターフェイスの言語で、ブラウザーはすべての言語で利用可能ではありません。ブラウザーが選択した言語にしたがってcultureを自動的に設定することを決める場合、かならず代わりの言語を選択する方法をユーザーに提供してください。</p>
</blockquote>

<a name="standards.and.formats" id="standards.and.formats"></a><h2>標準規格とフォーマット</h2>

<p>Webアプリケーションの内部では文化の特殊性が考慮されていません。たとえば、データベースはデータ、量などを保存する国際標準規格を使います。しかし、データがユーザーから送信されるもしくは読みとられる場合、変換を行う必要があります。ユーザーはタイムスタンプを理解しませんし、Frenchの代わりに母国語のFrançaisと表記することを望みます。ユーザーのcultureに基づいて、自動的に変換を行うための助けが必要です。</p>

<a name="outputting.data.in.the.users.culture" id="outputting.data.in.the.users.culture"></a><h3>ユーザーのcultureでデータを出力する</h3>

<p>いったんcultureが定義されると、これに依存するヘルパーは自動的に適切な出力を持ちます。リスト13-3で示されるように、たとえば、<code>format_number()</code>ヘルパーは自動的にユーザーが慣れ親しんでいる書式で数字を表示します。</p>

<p>リスト13-3 - ユーザーのcultureに合わせて数字を表示する</p>

<pre class="php"><span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Number'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
<span class="kw2">&lt;?php</span> <span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">setCulture</span><span class="br0">&#40;</span><span class="st_h">'en_US'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_number<span class="br0">&#40;</span><span class="nu19">12000.10</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; '12,000.10'
&nbsp;
<span class="kw2">&lt;?php</span> <span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">setCulture</span><span class="br0">&#40;</span><span class="st_h">'fr_FR'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_number<span class="br0">&#40;</span><span class="nu19">12000.10</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; '12 000,10'</pre>

<p>明示的にcultureをヘルパーに渡す必要はありません。ヘルパーは現在のセッションオブジェクトのなかでcultureを自分たち自身で探します。リスト13-4は出力に対してユーザーのcultureを考慮しているヘルパーの一覧です。</p>

<p>リスト13-4 - cultureに依存するヘルパー</p>

<pre class="php"><span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Date'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_date<span class="br0">&#40;</span><span class="kw3">time</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; '9/14/06'
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_datetime<span class="br0">&#40;</span><span class="kw3">time</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; 'September 14, 2006 6:11:07 PM CEST'
&nbsp;
<span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Number'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_number<span class="br0">&#40;</span><span class="nu19">12000.10</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; '12,000.10'
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_currency<span class="br0">&#40;</span><span class="nu0">1350</span><span class="sy0">,</span> <span class="st_h">'USD'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; '$1,350.00'
&nbsp;
<span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'I18N'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_country<span class="br0">&#40;</span><span class="st_h">'US'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; 'United States'
&nbsp;
<span class="kw2">&lt;?php</span> format_language<span class="br0">&#40;</span><span class="st_h">'en'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; 'English'
&nbsp;
<span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Form'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> input_date_tag<span class="br0">&#40;</span><span class="st_h">'birth_date'</span><span class="sy0">,</span> <span class="kw3">mktime</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="nu0">9</span><span class="sy0">,</span> <span class="nu0">14</span><span class="sy0">,</span> <span class="nu0">2006</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; input type=&quot;text&quot; name=&quot;birth_date&quot; id=&quot;birth_date&quot; value=&quot;9/14/06&quot; size=&quot;11&quot; /&gt;
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> select_country_tag<span class="br0">&#40;</span><span class="st_h">'country'</span><span class="sy0">,</span> <span class="st_h">'US'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
 =&gt; &lt;select name=&quot;country&quot; id=&quot;country&quot;&gt;&lt;option value=&quot;AF&quot;&gt;アフガニスタン&lt;/option&gt;
      ...
      &lt;option value=&quot;GB&quot;&gt;イギリス&lt;/option&gt;
      &lt;option value=&quot;US&quot; selected=&quot;selected&quot;&gt;アメリカ合衆国&lt;/option&gt;
      &lt;option value=&quot;UM&quot;&gt;合衆国領有小離島&lt;/option&gt;
      &lt;option value=&quot;UY&quot;&gt;ウルグアイ&lt;/option&gt;
      ...
    &lt;/select&gt;</pre>

<p>日付ヘルパーはcultureから独立した表示を強制する追加のフォーマットパラメーターを受けとることができますが、アプリケーションが国際化されている場合は使うべきではありません。</p>

<a name="getting.data.from.a.localized.input" id="getting.data.from.a.localized.input"></a><h3>ローカライズされた入力からデータを取得する</h3>

<p>データをユーザーのcultureに表示することが必要な場合、データを読みとることに関しては、可能なかぎり、すでに国際化されたデータを入力するようにアプリケーションのユーザーを後押しすべきです。このアプローチによって変化するフォーマットと不確定な地域によってデータを変換する方法を理解しなくてもすみます。たとえば、入力ボックスにコンマで区切られた通貨の値を入力しようとする人はいないでしょう。</p>

<p>実際のデータを隠す(<code>select_country_tag()</code>など)、もしくは複雑なデータの異なるコンポーネントをいくつかのシンプルな入力に分離することで、ユーザーの入力フォーマットをまとめることができます。</p>

<p>しかしながら、日付に関してこれを利用できないことがよくあります。ユーザーは自身の文化の書式で日付を入力することに慣れており、このようなデータは内部(と国際化)書式に変換できるようにする必要があります。これが<code>sfI18N</code>クラスを適用する事例です。リスト13-5はこのクラスの使いかたを示しています。</p>

<p>リスト13-5 - アクションでローカライズされた書式からデータを取得する</p>

<pre class="php"><span class="re0">$date</span><span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'birth_date'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$user_culture</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getCulture</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// タイムスタンプを取得する</span>
<span class="re0">$timestamp</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getI18N</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getTimestampForCulture</span><span class="br0">&#40;</span><span class="re0">$date</span><span class="sy0">,</span> <span class="re0">$user_culture</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 構造化データを取得する</span>
<span class="kw3">list</span><span class="br0">&#40;</span><span class="re0">$d</span><span class="sy0">,</span> <span class="re0">$m</span><span class="sy0">,</span> <span class="re0">$y</span><span class="br0">&#41;</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getI18N</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getDateForCulture</span><span class="br0">&#40;</span><span class="re0">$date</span><span class="sy0">,</span> <span class="re0">$user_culture</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<a name="text.information.in.the.database" id="text.information.in.the.database"></a><h2>データベース内のテキスト情報</h2>

<p>ローカライズされたアプリケーションはユーザーのcultureにしたがって異なる内容を提供します。たとえば、オンラインショップは製品を同じ価格で世界中に提供できますが、国ごとにカスタマイズされた説明が付属します。このことは、データベースは異なるバージョンのデータの任意の部分を保存可能でなければならず、そのためには、特定の方法でスキーマを設計しローカライズされたモデルオブジェクトを操作するたびにcultureを使う必要があります。</p>

<a name="creating.localized.schema" id="creating.localized.schema"></a><h3>ローカライズされたスキーマを作成する</h3>

<p>ローカライズされたいくつかのデータを含むそれぞれのテーブルに対して、テーブルを2つの部分に分割すべきです; 1つのテーブルは国際化カラムを持たず、一方のテーブルは国際化カラムだけを持ちます。2つのテーブルは一対多のリレーションによってリンクされます。モデルを変更しないことを求められたときに、このセットアップによってより多くの言語を追加できるようになります。たとえば<code>Product</code>テーブルを使うことを考えてみましょう。</p>

<p>リスト13-6で示されるように、最初は<code>schema.yml</code>ファイルのなかで　テーブルを作ります。</p>

<p>リスト13-6 - 国際化データのためのスキーマのサンプル(<code>config/schema.yml</code>)</p>

<pre><code>my_connection:
  my_product:
    _attributes: { phpName: Product, isI18N: true, i18nTable: my_product_i18n }
    id:          { type: integer, required: true, primaryKey: true, autoincrement: true }
    price:       { type: float }

  my_product_i18n:
    _attributes: { phpName: ProductI18n }
    id:          { type: integer, required: true, primaryKey: true, foreignTable: my_product, foreignReference: id }
    culture:     { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true }
    name:        { type: varchar, size: 50 }
</code></pre>

<p>最初のテーブルの<code>isI18N</code>属性と<code>i18nTable</code>属性と、2番目のテーブルの特別な<code>culture</code>カラムに注目してください。これらすべてはsymfony固有のPropelの強化機能です。</p>

<p>symfonyの自動化によってこれを書く作業がずっと速くなります。国際化データを含むテーブルがサフィックスとして<code>_i18n</code>を持つメインテーブルと同じ名前を持ち、それらが両方のテーブルで<code>id</code>という名前のカラムに関連する場合、メインテーブルに対するi18n属性と同様に、<code>_i18n</code>テーブルにおいて<code>id</code>カラムと<code>culture</code>カラムを省略できます; symfonyはこれらを推察します。このことはsymfonyがリスト13-7のスキーマをリスト13-6のスキーマと同じものとみなすことを意味します。</p>

<p>リスト13-7 - 省略形式の、国際化データのためのスキーマのサンプル(<code>config/schema.yml</code>)</p>

<pre><code>my_connection:
  my_product:
    _attributes: { phpName: Product }
    id:
    price:       float
  my_product_i18n:
    _attributes: { phpName: ProductI18n }
    name:        varchar(50)
</code></pre>

<a name="using.the.generated.i18n.objects" id="using.the.generated.i18n.objects"></a><h3>生成された国際化オブジェクトを使う</h3>

<p>いったん対応するオブジェクトモデルがビルドされると(<code>schema.yml</code>をそれぞれ修正した後に<code>propel:build-model</code>タスクを呼び出すことをお忘れなく)、リスト13-8で示されるように、あたかも1つのテーブルしか存在しないかのように、国際化をサポートする<code>Product</code>クラスを使うことができます。</p>

<p>リスト13-8 - 国際化オブジェクトを処理する</p>

<pre class="php"><span class="re0">$product</span> <span class="sy0">=</span> ProductPeer<span class="sy0">::</span><span class="me2">retrieveByPk</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$product</span><span class="sy0">-&gt;</span><span class="me1">setName</span><span class="br0">&#40;</span><span class="st_h">'Nom du produit'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// デフォルトでは、cultureは現在のユーザーのculture</span>
<span class="re0">$product</span><span class="sy0">-&gt;</span><span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw1">echo</span> <span class="re0">$product</span><span class="sy0">-&gt;</span><span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="st_h">'Nom du produit'</span>
&nbsp;
<span class="re0">$product</span><span class="sy0">-&gt;</span><span class="me1">setName</span><span class="br0">&#40;</span><span class="st_h">'Product name'</span><span class="sy0">,</span> <span class="st_h">'en'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// cultureの値を'en'に変更する</span>
<span class="re0">$product</span><span class="sy0">-&gt;</span><span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw1">echo</span> <span class="re0">$product</span><span class="sy0">-&gt;</span><span class="me1">getName</span><span class="br0">&#40;</span><span class="st_h">'en'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="st_h">'Product name'</span></pre>

<p>ピアオブジェクトを持つクエリに関しては、リスト13-10で示されるように、通常の<code>doSelect</code>の代わりに、<code>doSelectWithI18n</code>メソッドを使うことで現在のcultureのための翻訳内容を持つオブジェクトへの結果を制限できます。加えて、このメソッドは同時に通常のオブジェクトに関連する国際化オブジェクトを作成します。結果として全内容を取得するクエリの回数を減らすことになります(パフォーマンスにおけるこのメソッドのプラスの影響に関する詳細な情報は18章を参照)。</p>

<p>リスト13-10 - 国際化された<code>Criteria</code>でオブジェクトを取得する</p>

<pre class="php"><span class="re0">$c</span> <span class="sy0">=</span> <span class="kw2">new</span> Criteria<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$c</span><span class="sy0">-&gt;</span><span class="me1">add</span><span class="br0">&#40;</span>ProductPeer<span class="sy0">::</span><span class="me2">PRICE</span><span class="sy0">,</span> <span class="nu0">100</span><span class="sy0">,</span> Criteria<span class="sy0">::</span><span class="me2">LESS_THAN</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$products</span> <span class="sy0">=</span> ProductPeer<span class="sy0">::</span><span class="me2">doSelectWithI18n</span><span class="br0">&#40;</span><span class="re0">$c</span><span class="sy0">,</span> <span class="re0">$culture</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="co1">// $culture引数はオプション</span>
<span class="co1">// cultureが与えられていない場合現在のユーザーのcultureが使われる</span></pre>

<p>ですので、基本的には、国際化オブジェクトを直接処理する必要は決してありませんが、代わりに通常のオブジェクトを持つクエリを行うたびにモデルにcultureを渡します(もしくは推測させます)。</p>

<a name="interface.translation" id="interface.translation"></a><h2>インターフェイスの翻訳</h2>

<p>ユーザーのインターフェイスは国際化アプリケーションに適用させる必要があります。テンプレートは、同じプレゼンテーションによってですが、いくつかの言語でラベル、メッセージ、とナビゲーションを表示できます。symfonyは、デフォルトの言語でテンプレートを開発し、その上でテンプレート内部で使われる翻訳フレーズを辞書ファイルで提供することを推奨します。そういうわけで、翻訳内容を修正、追加、もしくは削除するたびにテンプレートを変更する必要はありません。</p>

<a name="configuring.translation" id="configuring.translation"></a><h3>設定の翻訳</h3>

<p>テンプレートはデフォルトでは翻訳されません。このことは、リスト13-11で示されるように、ほかのすべてに先駆けて<code>settings.yml</code>ファイルでテンプレート翻訳機能を有効にする必要があることを意味します。</p>

<p>リスト13-11 - インターフェイス翻訳を有効にする(<code>frontend/config/settings.yml</code>)</p>

<pre><code>all:
  .settings:
    i18n: on
</code></pre>

<a name="using.the.translation.helper" id="using.the.translation.helper"></a><h3>翻訳ヘルパーを使う</h3>

<p>英語がデフォルトの言語で、英語とフランス語のサイトを作りたい場合を例に挙げてみましょう。サイトを翻訳することを考えるまえに、おそらくはリスト13-12の例で示されるようなテンプレートを書くでしょう。</p>

<p>リスト13-12 - 単独の言語テンプレート</p>

<pre class="php">Welcome to our website. Today's date is <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_date<span class="br0">&#40;</span><span class="kw3">date</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>テンプレートの語句を翻訳するsymfonyのために、これらの語句は翻訳される文章として認識されなければなりません。これが国際化ヘルパーグループのメンバーである<code>__()</code>ヘルパー(2つのアンダースコア)の目的です。ですので、すべてのテンプレートは翻訳するためにこのような関数呼び出し内部で語句を閉じる必要があります。リスト13-12は、たとえば、リスト13-13のように修正できます(この章の後のほうの"複雑な翻訳ニーズを扱う"のセクションでご覧頂けます。そこではこの例の翻訳ヘルパーを呼び出すより優れた方法があります) 。</p>

<p>リスト13-13 - 多言語の準備ができているテンプレート</p>

<pre class="php"><span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'I18N'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'Welcome to our website.'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st0">&quot;Today's date is &quot;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_date<span class="br0">&#40;</span><span class="kw3">date</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<blockquote class="tip"><p>
  アプリケーションがすべてのページに対して国際化ヘルパーグループを使う場合、それぞれのテンプレートに対して<code>use_helper('I18N')</code>の繰り返しを避けるために、ヘルパーを<code>settings.yml</code>ファイルの<code>standard_helpers</code>設定に含めることはよいアイディアでしょう。</p>
</blockquote>

<a name="using.dictionary.files" id="using.dictionary.files"></a><h3>辞書ファイルを使う</h3>

<p><code>__()</code>関数が呼び出されるたびに、symfonyは現在のユーザーのcultureの辞書で引数の翻訳を探します。対応する語句が見つかったら、翻訳が送り戻され、レスポンスに表示されます。ですのでユーザーインターフェイスの翻訳は辞書ファイルに依存します。</p>

<p>辞書ファイルは<code>messages.[language code].xml</code>にしたがって名づけられたXLIFF(XML Localization Interchange File Format)で書かれており、アプリケーションの<code>i18n/</code>ディレクトリに保存されます。</p>

<p>XLIFFはXMLに基づく標準フォーマットです。よく知られているように、Webサイトにおいてテキストを参照し翻訳するためにサードパーティの翻訳ツールを利用できます。翻訳会社はこのようなファイルの扱いかたや、新しいXLIFFフォーマットの翻訳文を追加することでサイト全体を翻訳する方法を知っています。</p>

<blockquote class="tip"><p>
  XLIFF標準規格に加え、symfonyは辞書のために翻訳用のバックエンドツールもいくつか提供します: gettext、MySQL、SQLiteです。これらのバックエンドツールの設定に関する詳細な情報はAPIドキュメントを参照してください。</p>
</blockquote>

<p>リスト13-14はリスト13-13をフランス語に翻訳するために必要な<code>messages.fr.xml</code>ファイルのなかでXLIFF構文の例を示しています。</p>

<p>リスト13-14 - フランス語用のXLIFF辞書(<code>frontend/i18n/messages.fr.xml</code>)</p>

<pre class="xml"><span class="sc3"><span class="re1">&lt;?xml</span> <span class="re0">version</span>=<span class="st0">&quot;1.0&quot;</span> <span class="re2">?&gt;</span></span>
<span class="sc3"><span class="re1">&lt;xliff</span> <span class="re0">version</span>=<span class="st0">&quot;1.0&quot;</span><span class="re2">&gt;</span></span>
  <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">orginal</span>=<span class="st0">&quot;global&quot;</span> <span class="re0">source-language</span>=<span class="st0">&quot;en_US&quot;</span> <span class="re0">datatype</span>=<span class="st0">&quot;plaintext&quot;</span><span class="re2">&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;body<span class="re2">&gt;</span></span></span>
      <span class="sc3"><span class="re1">&lt;trans-unit</span> <span class="re0">id</span>=<span class="st0">&quot;1&quot;</span><span class="re2">&gt;</span></span>
        <span class="sc3"><span class="re1">&lt;source<span class="re2">&gt;</span></span></span>Welcome to our website.<span class="sc3"><span class="re1">&lt;/source<span class="re2">&gt;</span></span></span>
        <span class="sc3"><span class="re1">&lt;target<span class="re2">&gt;</span></span></span>Bienvenue sur notre site web.<span class="sc3"><span class="re1">&lt;/target<span class="re2">&gt;</span></span></span>
      <span class="sc3"><span class="re1">&lt;/trans-unit<span class="re2">&gt;</span></span></span>
      <span class="sc3"><span class="re1">&lt;trans-unit</span> <span class="re0">id</span>=<span class="st0">&quot;2&quot;</span><span class="re2">&gt;</span></span>
        <span class="sc3"><span class="re1">&lt;source<span class="re2">&gt;</span></span></span>Today's date is <span class="sc3"><span class="re1">&lt;/source<span class="re2">&gt;</span></span></span>
        <span class="sc3"><span class="re1">&lt;target<span class="re2">&gt;</span></span></span>La date d'aujourd'hui est <span class="sc3"><span class="re1">&lt;/target<span class="re2">&gt;</span></span></span>
      <span class="sc3"><span class="re1">&lt;/trans-unit<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;/body<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;/file<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/xliff<span class="re2">&gt;</span></span></span></pre>

<p><code>source-language</code>属性はつねにあなたのcultureのすべてのISOコードを含まなければなりません。それぞれの翻訳は一意的な<code>id</code>属性を持つ<code>trans-unit</code>タグで記述します。</p>

<p>デフォルトのユーザーのcultureによって(<code>en_US</code>に設定します)、語句は翻訳されず、<code>__()</code>の生の引数呼び出しが表示されます。リスト13-13の結果はリスト13-12に似ています。しかしながら、cultureが<code>fr_FR</code>もしくは<code>fr_BE</code>に変更された場合、リスト13-15のような<code>messages.fr.xml</code>ファイルからの翻訳が代わりに表示されます。</p>

<p>リスト13-15 - 翻訳されたテンプレート</p>

<pre class="php">Bienvenue sur notre site web. La date d'aujourd'hui est
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_date<span class="br0">&#40;</span><span class="kw3">date</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>追加翻訳が必要な場合、同じディレクトリに新しい<code>messages.XX.xml</code>翻訳ファイルを追加するだけです。</p>

<blockquote class="tip"><p>
  辞書ファイルを探し、それらを解析し、任意の文字列に対して正しい翻訳を見つけるにはある程度の時間がかかるので、symfonyはプロセスを加速するために内部キャッシュを利用します。デフォルトでは、このキャッシュはファイルシステムを使います。(たとえば、複数のサーバー間のキャッシュを共有するために)<code>factories.yml</code>(19章を参照)で国際化キャッシュのふるまいを設定できます。</p>
</blockquote>

<a name="managing.dictionaries" id="managing.dictionaries"></a><h3>辞書を管理する</h3>

<p><code>messages.XX.xml</code>ファイルが長すぎて読むのが大変になったら、つねに翻訳をテーマによって名づけられたいくつかの辞書ファイルに分割できます。たとえば、アプリケーションの<code>i18n/</code>ディレクトリのなかで<code>messages.fr.xml</code>ファイルをつぎの3つのファイルに分割できます:</p>

<ul>
<li><code>navigation.fr.xml</code></li>
<li><code>terms_of_service.fr.xml</code></li>
<li><code>search.fr.xml</code></li>
</ul>

<p>翻訳がデフォルトの<code>messages.XX.xml</code>ファイルですぐに見つからないのであれば、3番目の引数を使う<code>__()</code>ヘルパーを呼び出すたびにどの辞書を使うのか宣言しなければならないことに注意してください。たとえば、<code>navigation.fr.xml</code>の辞書で翻訳された文字列を出力するには、つぎのように書きます:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'Welcome to our website'</span><span class="sy0">,</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="st_h">'navigation'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>翻訳辞書を編成する別の方法はモジュールによって分割することです。アプリケーション全体に対して単独の<code>messages.XX.xml</code>ファイルを書く代わりに、<code>modules/[module_name]/i18n/</code>ディレクトリごとに1つのモジュールを書けます。このことによってモジュールはアプリケーションから独立したものになります。プラグイン(17章を参照)などを再利用したい場合に必要です。</p>

<p><strong>symfony 1.1の新しい機能</strong></p>

<p>国際化用の辞書を手動で更新するとよくエラーになりやすいので、symfonyはこのプロセスを自動化するタスクを提供します。 <code>i18n-extract</code>タスクは翻訳されるすべての文字列を抽出するためにsymfonyのアプリケーションを解析します。このタスクは引数としてアプリケーションとcultureを受けとります:</p>

<pre><code>&gt; php symfony i18n:extract frontend en
</code></pre>

<p>デフォルトでは、タスクは辞書を修正しないので、これは新旧の国際化された文字列の文字数を出力します。新しい文字列を辞書に追加するには、<code>--auto-save</code>オプションを渡します:</p>

<pre><code>&gt; php symfony i18n:extract --auto-save frontend en
</code></pre>

<p><code>--auto-delete</code>オプションを渡すことで自動的に古い文字列を削除することもできます:</p>

<pre><code>&gt; php symfony i18n:extract --auto-save --auto-delete frontend en
</code></pre>

<blockquote class="note"><p>
  現在のタスクは既知の制限をいくつか持ちます。これはデフォルトの<code>messages</code>辞書と、バックエンドに基づくファイル(<code>XLIFF</code>と<code>gettext</code>)に対してのみ動作します。このタスクはメインの<code>apps/frontend/i18n/messages.XX.xml</code>ファイルの文字列の保存と削除のみ行います。</p>
</blockquote>

<a name="handling.other.elements.requiring.translation" id="handling.other.elements.requiring.translation"></a><h3>翻訳が必要なそのほかの要素を扱う</h3>

<p>つぎの項目は翻訳が必要かもしれないそのほかの要素です:</p>

<ul>
<li><p>画像、テキストのドキュメント、もしくはほかのタイプのアセットもユーザーのcultureにしたがって変わることがあります。最良の例は実際は画像であるタイポグラフィを持つテキストの一部です。これらのために、ユーザーの<code>culture</code>の名前をとったサブディレクトリを作ることができます:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> image_tag<span class="br0">&#40;</span><span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">getCulture</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/myText.gif'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre></li>
<li><p>バリデーションファイルからのエラーメッセージは<code>__()</code>によって自動的に出力されるので、これらを翻訳する辞書に追加する必要があります。</p></li>
<li>symfonyのデフォルトページ(ページが見つからない、サーバーの内部エラー、アクセスの制限など)は英語なので、国際化アプリケーションで書き直さなければなりません。おそらくはアプリケーションにあなた独自の<code>default</code>モジュールを作成し、<code>__()</code>をテンプレートのなかで使うべきです。これらのページをカスタマイズする方法は19章を参照してください。</li>
</ul>

<a name="handling.complex.translation.needs" id="handling.complex.translation.needs"></a><h3>複雑な翻訳ニーズを扱う</h3>

<p>翻訳は<code>__()</code>の引数が完全な文である場合のみ意味をなします。しかしながら、単語と混ざったフォーマットもしくは変数を持つ場合、文をいくつかのチャンク(塊)に分割したくなることがありますが、ヘルパーを無意味な文に呼び出す結果になります。幸いにして、<code>__()</code>ヘルパーはトークン(字句)に基づく置き換え機能を提供します。この機能は翻訳者がより扱いやすい意味にある辞書を持つための助けになります。HTML整形と同じように、ヘルパー呼び出しで同じようにトークンをそのままにできます。リスト13-16は例を示しています。</p>

<p>リスト13-16 - コードを含むセンテンスを翻訳する</p>

<pre class="php">// 基本例
Welcome to all the &lt;b&gt;new&lt;/b&gt; users.&lt;br /&gt;
There are <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> count_logged<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span> persons logged.
&nbsp;
// テキストの翻訳機能を有効にするためのわるい方法
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'Welcome to all the'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&lt;b&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'new'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/b&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'users'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>.&lt;br /&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'There are'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> count_logged<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'persons logged'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
// テキストの翻訳機能を有効にするためのよい方法
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'Welcome to all the &lt;b&gt;new&lt;/b&gt; users'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span> &lt;br /&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> __<span class="br0">&#40;</span><span class="st_h">'There are %1% persons logged'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'%1%'</span> <span class="sy0">=&gt;</span> count_logged<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>この例では、トークンは<code>%1%</code>ですが、決して何でもよいわけではありません。 なぜなら翻訳ヘルパーが使う置き換え関数が<code>strtr()</code>だからです。</p>

<p>翻訳に関する共通問題の1つは複数形の使いかたです。結果の数に応じて、テキストは変わりますが、言語に従った同じ方法では変わりません。たとえば、リスト13-16の最後のセンテンスは<code>count_logged()</code>が<code>0</code>もしくは<code>1</code>を返す場合には正しくはありません。この関数の戻り値をテストし、使うセンテンスがどれなのかを選択できますが、これはたくさんのコードが必要になることを意味します。加えて、異なる言語は異なる文法のルールを持ち、複数形の語形変化のルールはとても複雑になる場合があります。この問題は非常にありふれたものなので、この問題に対処するためにsymfonyは<code>format_number_choice()</code>と呼ばれるヘルパーを提供します。リスト13-17はこのヘルパーを使う方法を示しています。</p>

<p>リスト13-17 - パラメーターの値に依存するセンテンスを翻訳する</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> format_number_choice<span class="br0">&#40;</span>
  <span class="st_h">'[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'%1%'</span> <span class="sy0">=&gt;</span> count_logged<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span> count_logged<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>最初の引数はテキストの複数の選択肢です。2番目の引数は(<code>__()</code>ヘルパーに関する)置き換えパターンのオプションです。3番目の引数はどのテキストが取られるのかを決めるテスト上の数字です。</p>

<p>つぎのような構文を使うことで、メッセージ/文字列の選択は許可される値の配列の後に続くパイプ(<code>|</code>)文字によって分離されます:</p>

<ul>
<li><code>[1,2]</code>: 1と2も含めて、1と2のあいだの値を許可する</li>
<li><code>(1,2)</code>: 1と2を除いて、1と2のあいだの値を許可する</li>
<li><code>{1,2,3,4}</code>: 集合で定義された値のみ許可する</li>
<li><code>[-Inf,0)</code>: 負の無限大よりも大きいもしくは厳密に0未満の値を許可する</li>
<li><code>{n: n % 10 &gt; 1 &amp;&amp; n % 10 &lt; 5} pliki</code>: 2、3、4、 22、23、24のような数字をマッチする(ポーランド語やロシア語のような言語に対して便利です) <strong>開発バージョンの新しい機能</strong></li>
</ul>

<p>角かっこと丸かっこの区切り文字の空ではない組み合わせが許容されます。</p>

<p>翻訳機能を適切に機能させるにはメッセージをXLIFFファイルに明示的に表示しなければなりません。リスト13-18は例を示しています。</p>

<p>リスト13-18 - <code>format_number_choice()</code>の引数のためのXLIFF辞書</p>

<pre><code>...
&lt;trans-unit id="3"&gt;
  &lt;source&gt;[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged&lt;/source&gt;
  &lt;target&gt;[0]Personne n'est connecté|[1]Une personne est connectée|(1,+Inf]Il y a %1% personnes en ligne&lt;/target&gt;
&lt;/trans-unit&gt;
...
</code></pre>

<blockquote class="sidebar"><p class="title">
  <strong>文字集合のための少しの説明</strong></p>
  
  <p>テンプレートで国際化された内容を処理することはしばし文字集合の問題につながります。ローカライズされた文字集合を使う場合、ユーザーがcultureを変更するたびに文字集合も変更することが必要になります。加えて、任意の文字集合で書かれたテンプレートはほかの文字集合の文字を正確に表示しません。</p>
  
  <p>複数のcultureを扱うと同時に、すべてのテンプレートがUTF-8で保存され、レイアウトがこの文字集合で内容を宣言しなければならない訳はそういうわけです。つねにUTF-8で扱えば、不愉快な不意打ちに合わなくてすみますし、やっかいな問題を悩まずにすみます。</p>
  
  <p>symfonyのアプリケーションは<code>settings.yml</code>ファイルのなかの字集合に関する1つのなか心的な設定に依存します。このパラメーターを変更することはすべてのレスポンスの<code>Content-Type</code>ヘッダーを変更することになります。</p>

<pre><code>all:
  .settings:
    charset: utf-8
</code></pre>
</blockquote>

<a name="calling.the.translation.helper.outside.a.template" id="calling.the.translation.helper.outside.a.template"></a><h3>テンプレート外部から翻訳ヘルパーを呼び出す</h3>

<p>ページに表示されるテキストのすべてがテンプレートによってもたらされるわけではありません。アプリケーションのほかの部分: アクション、フィルター、モデルクラスなどで、<code>__()</code>ヘルパーをしばし呼び出す必要があるのはそういうわけです。リスト13-19はContext Singletonを通して<code>I18N</code>オブジェクトの現在のインスタンスを読みとることでアクションでヘルパーを呼び出す方法を示しています。</p>

<p>リスト13-19 - アクションで<code>__()</code>を呼び出す</p>

<pre class="php"><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getI18N</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>__<span class="br0">&#40;</span><span class="re0">$text</span><span class="sy0">,</span> <span class="re0">$args</span><span class="sy0">,</span> <span class="st_h">'messages'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>ユーザーのcultureの扱いかたを理解しているのであればWebアプリケーションで国際化とローカライズする作業は苦痛をともなわずに行うことができます。ヘルパーはフォーマットされたデータを正しく出力する方法を自動的に考慮し、データベースから読みとられたローカライズされた内容はあたかもシンプルなテーブルの一部として見なされます。インターフェイスの翻訳に関しては、<code>__()</code>ヘルパーとXLIFFの辞書によって最小限の労力で最大限の結果が保証されます。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="12-Caching.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="14-Generators.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
